#include "input.hpp"

Input::Input(char* _file) {
	file.insert(0,_file);
}

Input::Input(QString _file) : file(_file) {

}

Input::~Input(void) {

}


void Input::parse(void) {
	QFile qfile(file);
	
	if(!qfile.open(QIODevice::ReadOnly)) {
		//QMessageBox::information(this, tr("Sirat"),tr("Cannot load %1.").arg(file_qstring));
	}
	else {
		QXmlStreamReader::setDevice(&qfile);
		list_widget = new QListWidget();
	
		while (!atEnd()) {
			readNext();
			if(isStartElement()) {
				if(name() == "raytracer") {
					raytracer = parse_raytracer();
				}
				if(name() == "grid") {
					grid = parse_grid();
				}
				if(name() == "scene") {
					scene = parse_scene();
				}
				
			}
		}
		if (hasError()) {
			cout << "XML ERROR:" << lineNumber() << ": " << errorString().toStdString() << endl;
			return;
		}
		
		if(raytracer == NULL) 
			cout << "Raytracer not found" << endl;
		if(grid != NULL) 
			raytracer->set_grid(grid);
		else 
			cout << "Grid not found" << endl;
		if(scene != NULL) 	
			raytracer->set_scene(scene);
		else
			cout << "Scene not found" << endl;
		
	}
}

void Input::save(void) {
	QFile qfile(file+".save");
	
	if(!qfile.open(QIODevice::WriteOnly)) {
		//QMessageBox::information(this, tr("Sirat"),tr("Cannot load %1.").arg(file_qstring));
	}
	else {
		QXmlStreamWriter::setDevice(&qfile);
		
		writeStartDocument();
		writeStartElement(QString("rendering"));
		
		write_scene();
		
		//write_raytracer();
		
		//write_grid();
		
		writeEndElement(); /* rendering */
		
		writeEndDocument();
	}
}

Scene* Input::get_scene(void) const {
	return scene;
}

Raytracer* Input::get_raytracer(void) const {
	return raytracer;
}

Grid* Input::get_grid(void) const {
	return grid;
}

QListWidget* Input::get_list_widget(void) const {
	return list_widget;
}

Vector Input::parse_vector(void) {
	double x = attributes().value("x").toString().toDouble();
	double y = attributes().value("y").toString().toDouble();
	double z = attributes().value("z").toString().toDouble();
	return Vector(x,y,z);
}

void Input::write_vector(const Vector& _vector) {
	double x = _vector.x;
	double y = _vector.y;
	double z = _vector.z;
	writeAttribute("x",tr("%1").arg(x));
	writeAttribute("y",tr("%1").arg(y));
	writeAttribute("z",tr("%1").arg(z));
}

Color Input::parse_color(void) {
	double r = attributes().value("r").toString().toDouble();
	double g = attributes().value("g").toString().toDouble();
	double b = attributes().value("b").toString().toDouble();
	return Color(r,g,b);
}

void Input::write_color(const Color& _color) {
	double r = _color.get_r();
	double g = _color.get_g();
	double b = _color.get_b();
	writeAttribute("r",tr("%1").arg(r));
	writeAttribute("g",tr("%1").arg(g));
	writeAttribute("b",tr("%1").arg(b));	
}


Material* Input::parse_material(void) {
	Material* material;
	Color ambient;
	Color diffuse;
	Color specular;
	double brightness;
	
	while(!(isEndElement() && name() == "material")) {
		readNext();
		if(isStartElement()) {
			if(name() == "ambient") {
				ambient = parse_color();
			}
			if(name() == "diffuse") {
				diffuse = parse_color();
			}
			if(name() == "specular") {
				specular = parse_color();
			}
			if(name() == "brightness") {
				brightness = attributes().value("value").toString().toDouble();
			}
		}
	}
	material = new Material(ambient,diffuse,specular,brightness);
	
	return material;

}

void Input::write_material(Object* _object) {
	Material* material = _object->get_material();
	Color ambient = material->get_ambient();
	Color diffuse = material->get_diffuse();
	Color specular = material->get_specular();
	double r,g,b;
	
	writeStartElement("material");
	
	writeStartElement("ambient");
	write_color(ambient);
	writeEndElement();
	
	writeStartElement("diffuse");
	write_color(diffuse);
	writeEndElement();
	
	writeStartElement("specular");
	write_color(specular);
	writeEndElement();
	
	writeEndElement();
	
}

vector<Object*>* Input::parse_objectlist(void) {
	/*Object attributes */
	vector<Object*>* objects = new vector<Object*>;
	Object* object;
	
	
	while(!(isEndElement() && name() == "objectlist")) {
		readNext();
		if(isStartElement()) {
			if(name() == "plane") {
				object = parse_plane();
			}
			if(name() == "sphere") {
				object = parse_sphere();
				
			}
		}
		if(isEndElement()) {
			if(name() == "plane") {
				objects->push_back(object);
				list_widget->addItem(QString("Plano"));
			}
			if(name() == "sphere") {
				objects->push_back(object);
				list_widget->addItem(QString("Esfera"));
			}
		}
		
	}
	return objects;
}

void Input::write_objectlist(void) {
	Object* object;
	Material* material = new Material(Color(),Color(),Color());
	Sphere* sphere = new Sphere(material,Vector(),0.0);
	Plane* plane;
	for(int i =0;i<scene->get_objectsEnd();i++) {
		object = scene->get_object(i);
		/* Check type */
		/* Sphere case */
		if(typeid(*sphere).name() == typeid(*object).name()) cout << "Esfera!" << endl;
		else cout << "DROGA" << endl;
		//write_sphere(static_cast<Sphere*> (object));
		/* Plane case */
		//write_plane(static_cast<Plane*> (object));
	}
	
}

Sphere* Input::parse_sphere(void) {
	/*Sphere attributes */
	Sphere* sphere;
	Vector center;
	double radius;
	Material* material;
	
	while(!(isEndElement() && name() == "sphere")) {
		readNext();
		if(isStartElement()) {
			if(name() == "center") {
				center = parse_vector();
			}
			if(name() == "radius") {
				radius = attributes().value("value").toString().toDouble();
			}
			if(name() == "material") {
				material = parse_material();
			}
		}
	}
	sphere = new Sphere(material,center,radius);
	return sphere;
}

void Input::write_sphere(Sphere* _sphere) {
	/*Sphere attributes */
	Vector center = _sphere->get_center();
	double radius = _sphere->get_radius();
	
	writeStartElement("sphere");
	
	write_material(_sphere);
	
	writeStartElement("center");
	write_vector(center);
	writeEndElement();
	
	writeStartElement("radius");
	writeAttribute("value",tr("%1").arg(radius));
	writeEndElement();
	
	writeEndElement();
}
Plane* Input::parse_plane(void) {
	/*Sphere attributes */
	Plane* plane;
	Vector point;
	Vector normal;
	Material* material;
	
	while(!(isEndElement() && name() == "plane")) {
		readNext();
		if(isStartElement()) {
			if(name() == "point") {
				point = parse_vector();
			}
			if(name() == "normal") {
				normal = parse_vector();
			}
			if(name() == "material") {
				material = parse_material();
			}
		}
	}
	plane = new Plane(material,point,normal);
	return plane;
}

void Input::write_plane(Plane* _plane) {
	/* Plane attributes */
	Vector point = _plane->get_point();
	Vector normal = _plane->get_normal(Vector());
	
	writeStartElement("plane");
		
	write_material(_plane);
	
	writeStartElement("point");
	write_vector(point);
	writeEndElement();
	
	writeStartElement("normal");
	write_vector(normal);
	writeEndElement();
	
	writeEndElement();
}

vector<Light*>* Input::parse_lightlist(void) {
	/* Light attributes */
	vector<Light*>* lightlist = new vector<Light*>;
	Light* light;
	
	
	while(!(isEndElement() && name() == "lightlist")) {
		readNext();
		if(isStartElement()) {
			if(name() == "light") {
				light = parse_light();
			}
		}
		if(isEndElement()) {
			if(name() == "light") {
				lightlist->push_back(light);
			}
		}
		
	}
	return lightlist;
}

Light* Input::parse_light(void) {
	/*Light attributes */
	Light* light;
	Vector position;
	Material* material;
	
	while(!(isEndElement() && name() == "light")) {
		readNext();
		if(isStartElement()) {
			if(name() == "position") {
				double x = attributes().value("x").toString().toDouble();
				double y = attributes().value("y").toString().toDouble();
				double z = attributes().value("z").toString().toDouble();
				position.set(x,y,z);
			}
			if(name() == "material") {
				material = parse_material();
			}
		}
	}
	light = new Light(material,position);
	return light;
}

Grid* Input::parse_grid(void) {
	/*Grid attributes */
	Vector position;
	unsigned short height,width;

	
	while(!(isEndElement() && name() == "grid")) {
		readNext();
		if(isStartElement()) {
			if(name() == "position") {
				position = parse_vector();
			}
			if(name() == "height") {
				height = attributes().value("value").toString().toUShort();
			}
			if(name() == "width") {
				width = attributes().value("value").toString().toUShort();
			}
		}
	}
	Grid* _grid = new Grid(width,height,position);
	return _grid;
}

Scene* Input::parse_scene(void) {
	scene = new Scene(Color(0.0,0.0,0.0),0.0);
	Color background;
	double ambient;
	double attenuation_a,attenuation_b,attenuation_c;

	while(!(isEndElement() && name() == "scene")) {
		readNext();
		if(isStartElement()) {
			if(name() == "background") {
				background = parse_color();
			}
			if(name() == "ambient") {
				ambient = attributes().value("value").toString().toDouble();
			}
			if(name() == "attenuation") {
				attenuation_a = attributes().value("a").toString().toDouble();
				attenuation_b = attributes().value("b").toString().toDouble();
				attenuation_c = attributes().value("c").toString().toDouble();
			}
		
			if(name() == "objectlist") {
				scene->add_objectlist(parse_objectlist());
			}
			if(name() == "lightlist") {
				scene->add_lightlist(parse_lightlist());
			}
		}
		if(isEndElement()) {
			if(name() == "attenuation") {
				scene->set_attenuation(attenuation_a,attenuation_b,attenuation_c);
			}
		}
	}
	scene->set_background(background);
	scene->set_ambient(ambient);
	return scene;
}

void Input::write_scene(void) {
	Color background = scene->get_background();
	double ambient = scene->get_ambient();
	double attenuation_a = scene->get_attenuation_value(1);
	double attenuation_b = scene->get_attenuation_value(2);
	double attenuation_c = scene->get_attenuation_value(3);

	writeStartElement("scene");
	
	writeStartElement("background");
	write_color(background);
	writeEndElement();
	
	writeStartElement("ambient");
	writeAttribute("value",tr("%1").arg(ambient));
	writeEndElement();
	
	writeStartElement("attenuation");
	writeAttribute("a",tr("%1").arg(attenuation_a));
	writeAttribute("b",tr("%1").arg(attenuation_b));
	writeAttribute("c",tr("%1").arg(attenuation_c));
	writeEndElement();
	
	write_objectlist();
	
	//write_lightlist();
	
	writeEndElement();
}

Raytracer* Input::parse_raytracer(void) {
	/* Raytracer attributes */
	Vector position;
	Vector up;
	unsigned short depth;
	
	while(!(isEndElement() && name() == "raytracer")) {
		readNext();
		if(isStartElement()) {
			if(name() == "position") {
				position = parse_vector();
			}
			if(name() == "up") {
				up = parse_vector();
			}
			if(name() == "depth") {
				depth = attributes().value("value").toString().toUShort();
			}
		}
	}
	Raytracer* _raytracer = new Raytracer(NULL,NULL,position,up,depth);
	return _raytracer;
}
void Input::write_raytracer(void) {

}


